package keyfun.loaders 
{
	import flash.filesystem.FileStream;
	import deng.fzip.*;
	import by.blooddy.crypto.MD5;
	import flash.utils.ByteArray;
	import flash.events.IOErrorEvent;
	import flash.events.Event;
	import flash.events.OutputProgressEvent;
	import flash.filesystem.FileMode;
	import flash.filesystem.File;
	import events.FileLoaderEvent;
	import flash.net.URLLoaderDataFormat;
	/**
	 * ...
	 * @author Key Hui
	 */
	public class ZipLoader 
	{
		private var unzipTarget:String;
		private var loadZipSuccessCallback:Function;
		private var loadZipErrorCallback:Function;
		private var loadZipProgressCallback:Function;
		
		private var fileZip:FZip;
		private var currentFileIdx:int;
		private var stream:FileStream;
		private var step:Number;
		private var zipRootFolderName:String;
		private var imageDataZipMD5:String = "0";
		
		private var fileLoader:FileLoader;
		
		public function ZipLoader(url:String, targetPath:String, successCallback:Function, errorCallback:Function, progressCallback:Function, checksum:String = "0") 
		{
			loadZipSuccessCallback = successCallback;
			loadZipErrorCallback = errorCallback;
			loadZipProgressCallback = progressCallback;
			imageDataZipMD5 = checksum;
			
			unzipTarget = targetPath;
			
			fileLoader = new FileLoader(url, URLLoaderDataFormat.BINARY);
			fileLoader.addEventListener(FileLoaderEvent.ON_COMPLETE, onCompleLoadZIP);
			fileLoader.addEventListener(FileLoaderEvent.ON_ERROR, onErrorLoadZIP);
			fileLoader.addEventListener(FileLoaderEvent.ON_LOADING, onProgressZIP);
		}
		
		private function onProgressZIP(e:FileLoaderEvent):void 
		{
			trace("loaded: " + e.bytesLoaded + " / " + e.bytesTotal);
			loadZipProgressCallback(e.bytesLoaded, e.bytesTotal);
		}
		
		private function onCompleLoadZIP(e:FileLoaderEvent):void 
		{
			trace("onCompleLoadZIP");
			var bytes:ByteArray = ByteArray(e.fileData);
			//this checksum works only when using adhoc mode on mobile for unknown reason
			
			if (imageDataZipMD5 == "" || imageDataZipMD5 == null)
			{
				trace("No Checksum found");
				return;
			}
			
			var md5:String = MD5.hashBytes(bytes);
			trace("md5:  " + md5);
			trace("imageDataZipMD5: " + imageDataZipMD5);
			
			if (md5 != imageDataZipMD5) 
			{
				trace("not equal!!!");
				loadZipErrorCallback();
				return;
			}
			
			var fZip:FZip = new FZip();
			fZip.addEventListener(Event.COMPLETE, loadZipComplete);
			fZip.addEventListener(IOErrorEvent.IO_ERROR, loadZipError);
			fZip.addEventListener(FZipErrorEvent.PARSE_ERROR, parseZipError);
			// load the zip to RAM and save the files one by one to document
			fZip.loadBytes(bytes);
		}

		private function loadZipComplete(e:Event):void 
		{
			trace("loadZipComplete");
			fileZip = e.target as FZip;
			currentFileIdx = 0;
			step = Math.ceil(100 / fileZip.getFileCount());
			
			loadNextFile();
		}
		
		private function loadNextFile():void 
		{
			trace("loadNextFile");
			trace("currentFileIdx: " + currentFileIdx);
			trace("fileZip.getFileCount(): " + fileZip.getFileCount());
			
			if (currentFileIdx < fileZip.getFileCount()) {
				trace("1");
				var zfile:FZipFile = fileZip.getFileAt(currentFileIdx);
				if (currentFileIdx == 0) {
					trace("zipRootFolderName: " + zipRootFolderName);
					zipRootFolderName = zfile.filename;
				}
				var file:File = File.documentsDirectory.resolvePath(unzipTarget + "/" + zfile.filename);
				trace("filename: " + zfile.filename);
				var str:String = "Thumbs.db";
				trace("zfile.filename.indexOf(str): " + zfile.filename.indexOf(str));
				trace("zfile.filename.length - str.length: " + (zfile.filename.length - str.length));
				if (zfile.filename.indexOf(str) == zfile.filename.length - str.length) 
				{
					trace("in!!");
					loadZipNextStep();
					return;
				}
				if (zfile.filename.charAt(zfile.filename.length - 1) != "/") 
				{
					trace("create file: " + zfile.content.length);
					stream = new FileStream();
					trace(file.url);
					stream.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, savingProgress);
					stream.openAsync(file, FileMode.WRITE);
					stream.writeBytes(zfile.content, 0, zfile.content.length);
					
					if (zfile.content.length == 0)
					{
						stream.close();
						loadZipNextStep();
					}
				} 
				else 
				{
					trace("create directory");
					if (!file.exists) file.createDirectory();
					loadZipNextStep();
				}
			} 
			else 
			{
				fileZip.close();
				loadZipSuccessCallback(zipRootFolderName);
			}
		}
		
		private function savingProgress(e:OutputProgressEvent):void 
		{
			trace("savingProgress");
			if (e.bytesPending == 0) 
			{
				stream.close();
				loadZipNextStep();
			}
		}
		
		private function loadZipNextStep():void 
		{
			currentFileIdx++;
			loadNextFile();
		}
		
		private function loadZipError(e:IOErrorEvent):void 
		{
			trace("loadZipError");
			loadZipErrorCallback();
		}
		
		private function parseZipError(e:FZipErrorEvent):void 
		{
			trace("parseZipError");
			loadZipErrorCallback();
		}
		
		private function onErrorLoadZIP(e:FileLoaderEvent):void 
		{
			trace("onErrorLoadZIP");
			loadZipErrorCallback();
		}
		
	}

}